﻿// Decompiled with JetBrains decompiler
// Type: Netick.CodeGen.RpcProcessor
// Assembly: Netick.CodeGen, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 95965C71-875F-44B8-BA79-93A19C7D6A17
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Editor\Netick.CodeGen.dll

using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Mono.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

#nullable disable
namespace Netick.CodeGen;

internal sealed class RpcProcessor
{
  private TypeReference NetickRPC_Type;
  private TypeReference NetworkScript_Type;
  private TypeReference Engine_Type;
  private TypeReference PlayerId_Type;
  private TypeReference RpcContext_Type;
  private TypeDefinition InternalNetworkRpcSize;
  private MethodReference InternalNetworkRpcSizeConstructor;
  private MethodReference NetworkPlayerId_Invalid;
  private MethodReference NetworkRpc_CreateRPC;
  private MethodReference NetworkRpc_CanSend;
  private MethodReference NetworkRpc_CanInvokeWithRpcData;
  private MethodReference NetworkRpc_CanInvokeWithoutRpcData;
  private MethodReference NetworkRpc_IsResim;
  private MethodReference NetworkBehaviour_Entity;
  private MethodReference NetworkBehaviour_Engine;
  private MethodReference IntPtr_ToPointer;
  private TypeReference IntPtrRef;
  private TypeReference IntPointer;
  private ICodeGenUser CodeGenUser;

  internal void Init(ICodeGenUser user, ModuleDefinition moduleDefinition)
  {
    this.CodeGenUser = user;
    this.NetickRPC_Type = moduleDefinition.ImportReference(typeof (NetworkRpc));
    this.NetworkScript_Type = moduleDefinition.ImportReference(typeof (INetickNetworkScript));
    this.Engine_Type = moduleDefinition.ImportReference(typeof (NetickEngine));
    this.PlayerId_Type = moduleDefinition.ImportReference(typeof (NetworkPlayerId));
    this.RpcContext_Type = moduleDefinition.ImportReference(typeof (RpcContext));
    this.InternalNetworkRpcSize = moduleDefinition.ImportReference(typeof (InternalNetworkRpcInfo)).Resolve();
    this.InternalNetworkRpcSizeConstructor = moduleDefinition.ImportReference((MethodReference) TypeDefinitionRocks.GetConstructors(this.InternalNetworkRpcSize).FirstOrDefault<MethodDefinition>());
    this.NetworkPlayerId_Invalid = moduleDefinition.ImportReference((MethodBase) typeof (NetworkPlayerId).GetProperty("Invalid").GetMethod);
    this.NetworkRpc_IsResim = moduleDefinition.ImportReference((MethodBase) typeof (NetworkRpc).GetMethod("InternalIsResim"));
    this.NetworkRpc_CanSend = moduleDefinition.ImportReference((MethodBase) typeof (NetworkRpc).GetMethod("InternalCanSend"));
    this.NetworkRpc_CanInvokeWithoutRpcData = moduleDefinition.ImportReference((MethodBase) typeof (NetworkRpc).GetMethod("InternalCanInvoke"));
    this.NetworkRpc_CanInvokeWithRpcData = moduleDefinition.ImportReference((MethodBase) typeof (NetworkRpc).GetMethod("InternalCanInvokeWithRID"));
    this.NetworkRpc_CreateRPC = moduleDefinition.ImportReference((MethodBase) typeof (NetworkRpc).GetMethod("InternalCreateRpc"));
    this.NetworkBehaviour_Entity = moduleDefinition.ImportReference((MethodBase) typeof (INetickNetworkScript).GetProperty("Entity").GetMethod);
    this.NetworkBehaviour_Engine = moduleDefinition.ImportReference((MethodBase) typeof (INetickScript).GetProperty("Engine").GetMethod);
    this.IntPtr_ToPointer = moduleDefinition.ImportReference((MethodBase) typeof (IntPtr).GetMethod("ToPointer"));
    this.IntPtrRef = moduleDefinition.ImportReference(typeof (IntPtr));
    this.IntPointer = moduleDefinition.ImportReference(typeof (int*));
  }

  public void Process(TypeDefinition typeDefinition)
  {
    foreach (MethodDefinition methodDefinition in ((IEnumerable<MethodDefinition>) typeDefinition.Methods).ToList<MethodDefinition>())
    {
      foreach (CustomAttribute rpcAttr in ((IEnumerable<CustomAttribute>) methodDefinition.CustomAttributes).ToList<CustomAttribute>())
      {
        if (((MemberReference) rpcAttr.AttributeType).FullName == typeof (Rpc).FullName)
        {
          int stableHashCode = RpcProcessor.GetMethodName(methodDefinition).GetStableHashCode();
          this.ProcessRpc(typeDefinition, methodDefinition, rpcAttr, stableHashCode);
        }
      }
    }
  }

  private void ProcessRpc(
    TypeDefinition beh,
    MethodDefinition rpcMethod,
    CustomAttribute rpcAttr,
    int methodHash)
  {
    bool hasRpcInvokePara = false;
    bool isTargetRpc = false;
    if (((MethodReference) rpcMethod).Parameters.Count >= 1)
    {
      if (((ParameterReference) ((MethodReference) rpcMethod).Parameters[0]).ParameterType.Resolve() == this.PlayerId_Type.Resolve() && this.IsRpcTargetPara(((MethodReference) rpcMethod).Parameters[0]))
        isTargetRpc = true;
      if (((MethodReference) rpcMethod).Parameters.Count >= 2 && ((ParameterReference) ((MethodReference) rpcMethod).Parameters[0]).ParameterType.Resolve() == this.Engine_Type.Resolve() && ((ParameterReference) ((MethodReference) rpcMethod).Parameters[1]).ParameterType.Resolve() == this.PlayerId_Type.Resolve() && this.IsRpcTargetPara(((MethodReference) rpcMethod).Parameters[1]))
        isTargetRpc = true;
      if (((ParameterReference) ((MethodReference) rpcMethod).Parameters[((MethodReference) rpcMethod).Parameters.Count - 1]).ParameterType.Resolve() == this.RpcContext_Type.Resolve())
        hasRpcInvokePara = true;
    }
    this.InjectRpcCode(beh, rpcMethod, rpcAttr, methodHash, isTargetRpc, hasRpcInvokePara);
    MethodDefinition rpcReaderMethod = new MethodDefinition(((MemberReference) rpcMethod).Name + "__rpcInvoker", (MethodAttributes) 145, ((MemberReference) rpcMethod).Module.TypeSystem.Void);
    ((MethodReference) rpcReaderMethod).Parameters.Add(new ParameterDefinition("engine", (ParameterAttributes) 0, this.Engine_Type));
    ((MethodReference) rpcReaderMethod).Parameters.Add(new ParameterDefinition("script", (ParameterAttributes) 0, this.NetworkScript_Type));
    ((MethodReference) rpcReaderMethod).Parameters.Add(new ParameterDefinition("rpcContext", (ParameterAttributes) 0, this.RpcContext_Type));
    ((MethodReference) rpcReaderMethod).Parameters.Add(new ParameterDefinition("targetPlayer", (ParameterAttributes) 0, this.PlayerId_Type));
    ((MethodReference) rpcReaderMethod).Parameters.Add(new ParameterDefinition("dataPointer", (ParameterAttributes) 0, this.IntPtrRef));
    this.OverrideRpcReader(beh, rpcReaderMethod, rpcMethod, ((MethodReference) rpcMethod).Parameters.ToArray(), isTargetRpc, hasRpcInvokePara);
    beh.Methods.Add(rpcReaderMethod);
  }

  private void InjectRpcCode(
    TypeDefinition beh,
    MethodDefinition rpcMethod,
    CustomAttribute rpcAttribute,
    int methodHash,
    bool isTargetRpc,
    bool hasRpcInvokePara)
  {
    bool isStatic = rpcMethod.IsStatic;
    rpcMethod.Body.InitLocals = true;
    FieldReference fieldReference = ((MemberReference) rpcMethod).Module.ImportReference(typeof (NetworkRpc).GetField("Data"));
    TypeSystem typeSystem = ((MemberReference) rpcMethod).Module.TypeSystem;
    List<Instruction> instructions = new List<Instruction>(10);
    ILProcessor il = rpcMethod.Body.GetILProcessor();
    CustomAttributeArgument constructorArgument1 = rpcAttribute.ConstructorArguments[0];
    RpcPeers rpcPeers1 = (RpcPeers) ((CustomAttributeArgument) ref constructorArgument1).Value;
    CustomAttributeArgument constructorArgument2 = rpcAttribute.ConstructorArguments[1];
    RpcPeers rpcPeers2 = (RpcPeers) ((CustomAttributeArgument) ref constructorArgument2).Value;
    CustomAttributeArgument constructorArgument3 = rpcAttribute.ConstructorArguments[2];
    bool flag1 = (bool) ((CustomAttributeArgument) ref constructorArgument3).Value;
    CustomAttributeArgument constructorArgument4 = rpcAttribute.ConstructorArguments[3];
    bool flag2 = (bool) ((CustomAttributeArgument) ref constructorArgument4).Value;
    Instruction instruction1 = rpcMethod.Body.Instructions[rpcMethod.Body.Instructions.Count - 1];
    int num1 = 0;
    for (int index = 0; index < ((MethodReference) rpcMethod).Parameters.Count; ++index)
    {
      if (((MemberReference) ((ParameterReference) ((MethodReference) rpcMethod).Parameters[index]).ParameterType).ContainsGenericParameter)
      {
        this.CodeGenUser.LogError((object) (((MemberReference) ((ParameterReference) ((MethodReference) rpcMethod).Parameters[index]).ParameterType).FullName + ": you are not allowed to use generic parameters on RPCs."));
      }
      else
      {
        if (((MemberReference) ((ParameterReference) ((MethodReference) rpcMethod).Parameters[index]).ParameterType).FullName == ((MemberReference) ((MemberReference) rpcMethod).Module.TypeSystem.String).FullName)
          this.CodeGenUser.LogError((object) $"{((MemberReference) rpcMethod).FullName} on {((MemberReference) beh).Name}: you are not allowed to use a regular string as an RPC parameter. Use NetworkString instead.");
        if (isStatic && index == 0 && ((ParameterReference) ((MethodReference) rpcMethod).Parameters[0]).ParameterType.Resolve() != this.Engine_Type.Resolve())
          this.CodeGenUser.LogError((object) $"{((MemberReference) rpcMethod).FullName} on {((MemberReference) beh).Name}: you are not allowed to use a reference type as an RPC parameter.");
        if (isStatic)
        {
          if (index > 0 && !((ParameterReference) ((MethodReference) rpcMethod).Parameters[index]).ParameterType.IsValueType)
            this.CodeGenUser.LogError((object) $"{((MemberReference) rpcMethod).FullName} on {((MemberReference) beh).Name}: you are not allowed to use a reference type as an RPC parameter.");
        }
        else if (!((ParameterReference) ((MethodReference) rpcMethod).Parameters[index]).ParameterType.IsValueType)
          this.CodeGenUser.LogError((object) $"{((MemberReference) rpcMethod).FullName} on {((MemberReference) beh).Name}: you are not allowed to use a reference type as an RPC parameter.");
        if ((!isStatic || index != 0) && (!(!isStatic & isTargetRpc) || index != 0) && (!(isStatic & isTargetRpc) || index != 1) && !(index == ((MethodReference) rpcMethod).Parameters.Count - 1 & hasRpcInvokePara))
          num1 += this.GetPropertySizeWords(((ParameterReference) ((MethodReference) rpcMethod).Parameters[index]).ParameterType);
      }
    }
    CustomAttribute customAttribute = new CustomAttribute(this.InternalNetworkRpcSizeConstructor);
    customAttribute.ConstructorArguments.Add(new CustomAttributeArgument(((MemberReference) rpcMethod).Module.TypeSystem.Int32, (object) num1));
    customAttribute.ConstructorArguments.Add(new CustomAttributeArgument(((MemberReference) rpcMethod).Module.TypeSystem.Boolean, (object) isTargetRpc));
    customAttribute.ConstructorArguments.Add(new CustomAttributeArgument(((MemberReference) rpcMethod).Module.TypeSystem.Boolean, (object) isStatic));
    rpcMethod.CustomAttributes.Add(customAttribute);
    il.Body.Variables.Add(new VariableDefinition(this.NetickRPC_Type));
    int count = il.Body.Variables.Count;
    Instruction instruction2 = il.Create(OpCodes.Ldarg_0);
    Instruction instruction3 = il.Create(OpCodes.Ldarg_0);
    instructions.Add(il.Create(OpCodes.Ldarg_0));
    if (!isStatic)
      instructions.Add(il.Create(OpCodes.Callvirt, this.NetworkBehaviour_Engine));
    instructions.Add(il.Create(OpCodes.Call, this.NetworkRpc_IsResim));
    instructions.Add(il.Create(OpCodes.Brtrue, instruction1));
    instructions.Add(instruction2);
    if (!isStatic)
    {
      instructions.Add(il.Create(OpCodes.Callvirt, this.NetworkBehaviour_Engine));
      instructions.Add(il.Create(OpCodes.Ldarg_0));
      instructions.Add(il.Create(OpCodes.Callvirt, this.NetworkBehaviour_Entity));
    }
    else
      instructions.Add(il.Create(OpCodes.Ldnull));
    instructions.Add(il.Create(OpCodes.Ldc_I4, (int) rpcPeers1));
    instructions.Add(il.Create(OpCodes.Ldc_I4, (int) rpcPeers2));
    instructions.Add(il.Create(OpCodes.Ldc_I4, methodHash));
    instructions.Add(il.Create(OpCodes.Call, this.NetworkRpc_CanSend));
    instructions.Add(il.Create(OpCodes.Brfalse, instruction3));
    instructions.Add(il.Create(OpCodes.Ldarg_0));
    if (!isStatic)
      instructions.Add(il.Create(OpCodes.Callvirt, this.NetworkBehaviour_Engine));
    instructions.Add(il.Create(OpCodes.Ldc_I4, isTargetRpc ? 1 : 0));
    if (isTargetRpc)
      instructions.Add(il.Create(OpCodes.Ldarg_1));
    else
      instructions.Add(il.Create(OpCodes.Call, this.NetworkPlayerId_Invalid));
    instructions.Add(il.Create(OpCodes.Ldc_I4, methodHash));
    if (!isStatic)
      instructions.Add(il.Create(OpCodes.Ldarg_0));
    else
      instructions.Add(il.Create(OpCodes.Ldnull));
    instructions.Add(il.Create(OpCodes.Ldc_I4, flag1 ? 1 : 0));
    instructions.Add(il.Create(OpCodes.Ldc_I4, (int) rpcPeers2));
    instructions.Add(il.Create(OpCodes.Ldc_I4, num1));
    instructions.Add(il.Create(OpCodes.Call, this.NetworkRpc_CreateRPC));
    List<Instruction> instructionList = new List<Instruction>();
    int num2 = 0;
    bool flag3 = false;
    int num3 = 0;
    for (int index = 0; index < ((MethodReference) rpcMethod).Parameters.Count; ++index)
    {
      ParameterDefinition parameter = ((MethodReference) rpcMethod).Parameters[index];
      int num4 = index + 1;
      if (isStatic)
        num4 = index;
      if ((!isStatic || index != 0) && (!(!isStatic & isTargetRpc) || index != 0) && (!(isStatic & isTargetRpc) || index != 1) && !(parameter == ((IEnumerable<ParameterDefinition>) ((MethodReference) rpcMethod).Parameters).Last<ParameterDefinition>() & hasRpcInvokePara))
      {
        ++num3;
        instructions.Add(il.Create(OpCodes.Dup));
        flag3 = true;
        instructions.Add(il.Create(OpCodes.Ldfld, fieldReference));
        instructions.Add(il.Create(OpCodes.Ldc_I4, num2));
        instructions.Add(il.Create(OpCodes.Conv_I));
        instructions.Add(il.Create(OpCodes.Ldc_I4_4));
        instructions.Add(il.Create(OpCodes.Mul));
        instructions.Add(il.Create(OpCodes.Add));
        instructions.Add(il.Create(OpCodes.Ldarg, num4));
        WeaverUtils.InsertStind(((ParameterReference) parameter).ParameterType, instructions, il);
        num2 += this.GetPropertySizeWords(((ParameterReference) parameter).ParameterType);
      }
    }
    if (((flag3 || ((MethodReference) rpcMethod).Parameters.Count == 0 ? 1 : (num3 == 0 ? 1 : 0)) | (hasRpcInvokePara ? 1 : 0)) != 0)
      instructions.Add(il.Create(OpCodes.Pop));
    instructions.Add(instruction3);
    if (!isStatic)
    {
      instructions.Add(il.Create(OpCodes.Callvirt, this.NetworkBehaviour_Engine));
      instructions.Add(il.Create(OpCodes.Ldarg_0));
      instructions.Add(il.Create(OpCodes.Callvirt, this.NetworkBehaviour_Entity));
    }
    else
      instructions.Add(il.Create(OpCodes.Ldnull));
    instructions.Add(il.Create(OpCodes.Ldc_I4, (int) rpcPeers1));
    instructions.Add(il.Create(OpCodes.Ldc_I4, (int) rpcPeers2));
    instructions.Add(il.Create(OpCodes.Ldc_I4, flag2 ? 1 : 0));
    instructions.Add(il.Create(OpCodes.Ldc_I4, methodHash));
    instructions.Add(il.Create(OpCodes.Ldc_I4, isTargetRpc ? 1 : 0));
    if (isTargetRpc)
      instructions.Add(il.Create(OpCodes.Ldarg, 1));
    else
      instructions.Add(il.Create(OpCodes.Call, this.NetworkPlayerId_Invalid));
    if (hasRpcInvokePara)
    {
      instructions.Add(il.Create(OpCodes.Ldarga, isStatic ? ((ParameterReference) ((IEnumerable<ParameterDefinition>) ((MethodReference) rpcMethod).Parameters).Last<ParameterDefinition>()).Index : ((ParameterReference) ((IEnumerable<ParameterDefinition>) ((MethodReference) rpcMethod).Parameters).Last<ParameterDefinition>()).Index + 1));
      instructions.Add(il.Create(OpCodes.Call, this.NetworkRpc_CanInvokeWithRpcData));
    }
    else
      instructions.Add(il.Create(OpCodes.Call, this.NetworkRpc_CanInvokeWithoutRpcData));
    instructions.Add(il.Create(OpCodes.Brfalse, instruction1));
    instructions.Reverse();
    instructions.ForEach((Action<Instruction>) (instruction => il.Body.Instructions.Insert(0, instruction)));
  }

  private void OverrideRpcReader(
    TypeDefinition beh,
    MethodDefinition rpcReaderMethod,
    MethodDefinition rpcMethod,
    ParameterDefinition[] rpcParas,
    bool isTargetRpc,
    bool hasRpcInvokePara)
  {
    bool isStatic = rpcMethod.IsStatic;
    ILProcessor ilProcessor = rpcReaderMethod.Body.GetILProcessor();
    rpcReaderMethod.Body.Variables.Add(new VariableDefinition(this.IntPointer));
    Collection<Instruction> instructions = rpcReaderMethod.Body.Instructions;
    instructions.Clear();
    if (!isStatic)
    {
      TypeReference typeReference = ((MemberReference) beh).Module.ImportReference((TypeReference) beh);
      instructions.Add(ilProcessor.Create(OpCodes.Ldarg_1));
      instructions.Add(ilProcessor.Create(OpCodes.Castclass, typeReference));
    }
    instructions.Add(ilProcessor.Create(OpCodes.Ldarga_S, (byte) 4));
    instructions.Add(ilProcessor.Create(OpCodes.Call, this.IntPtr_ToPointer));
    instructions.Add(ilProcessor.Create(OpCodes.Stloc_0));
    if (isStatic)
      instructions.Add(ilProcessor.Create(OpCodes.Ldarg_0));
    if (isTargetRpc)
      instructions.Add(ilProcessor.Create(OpCodes.Ldarg_3));
    int num = 0;
    for (int index = 0; index < rpcParas.Length; ++index)
    {
      ParameterDefinition rpcPara = rpcParas[index];
      if ((!isStatic || index != 0) && (!(isStatic & isTargetRpc) || index != 1) && (!(!isStatic & isTargetRpc) || index != 0) && !(index == rpcParas.Length - 1 & hasRpcInvokePara))
      {
        instructions.Add(ilProcessor.Create(OpCodes.Ldloc_0));
        instructions.Add(ilProcessor.Create(OpCodes.Ldc_I4, num));
        instructions.Add(ilProcessor.Create(OpCodes.Conv_I));
        instructions.Add(ilProcessor.Create(OpCodes.Ldc_I4_4));
        instructions.Add(ilProcessor.Create(OpCodes.Mul));
        instructions.Add(ilProcessor.Create(OpCodes.Add));
        WeaverUtils.InsertLdind(((ParameterReference) rpcPara).ParameterType, instructions, ilProcessor);
        num += this.GetPropertySizeWords(((ParameterReference) rpcPara).ParameterType);
      }
    }
    if (hasRpcInvokePara)
      instructions.Add(ilProcessor.Create(OpCodes.Ldarg_2));
    if (!isStatic)
      instructions.Add(ilProcessor.Create(OpCodes.Callvirt, (MethodReference) rpcMethod));
    else
      instructions.Add(ilProcessor.Create(OpCodes.Call, (MethodReference) rpcMethod));
    instructions.Add(ilProcessor.Create(OpCodes.Ret));
  }

  private static string GetMethodName(MethodDefinition method)
  {
    string methodName = ((MemberReference) method.DeclaringType).FullName + ((MemberReference) method).Name;
    Collection<ParameterDefinition> parameters = ((MethodReference) method).Parameters;
    for (int index = 0; index < parameters.Count; ++index)
      methodName += ((MemberReference) ((ParameterReference) parameters[index]).ParameterType).Name;
    return methodName;
  }

  private bool IsRpcTargetPara(ParameterDefinition para)
  {
    return ((IEnumerable<CustomAttribute>) para.CustomAttributes).Where<CustomAttribute>((Func<CustomAttribute, bool>) (x => ((MemberReference) x.AttributeType).FullName == typeof (RpcTarget).FullName)).FirstOrDefault<CustomAttribute>() != null;
  }

  private int GetPropertySizeWords(TypeReference type)
  {
    return NetickUtils.GetWordSize((int) WeaverUtils.SizeOf(type, this.CodeGenUser));
  }
}
